/**
 * 
 */
package javapop.framework.generic.tool;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javapop.framework.DefaultParseContext;
import javapop.framework.ParseInput;
import javapop.framework.ParseResult;
import javapop.framework.generic.GChoice;
import javapop.framework.generic.GDefRegle;
import javapop.framework.generic.GExprBracket;
import javapop.framework.generic.GExprOperand;
import javapop.framework.generic.GExprOperator;
import javapop.framework.generic.GExprParser;
import javapop.framework.generic.GIgnore;
import javapop.framework.generic.GKeyword;
import javapop.framework.generic.GKeywords;
import javapop.framework.generic.GListOf;
import javapop.framework.generic.GLiteral;
import javapop.framework.generic.GMaybe;
import javapop.framework.generic.GMaybeOrDefault;
import javapop.framework.generic.GMeta;
import javapop.framework.generic.GParser;
import javapop.framework.generic.GRefParser;
import javapop.framework.generic.GRegexp;
import javapop.framework.generic.GRegexpWithoutKeywords;
import javapop.framework.generic.GRegle;
import javapop.framework.generic.GTransform;
import javapop.framework.generic.GTuple;
import javapop.framework.generic.GenericGrammar;
import javapop.framework.generic.GrammarTree;
import javapop.framework.input.StringParseInput;


/**
 * Usefull for creating the grammar interface used for transformation.
 * 
 * @author Hakim Belhaouari
 *
 */
public class BuildGrammarTransformerClass implements GrammarVisitor<String, String, GrammarVisitorException> {
/*
 * idealement j'aurais voulu utiliser directement mon AEntity pour que tout soit automatique mais je n'ai pas l'impression que la dépendance
 * soit necessaire. 
 * 
 * je dois parcourir tous les p arseurs po ur récolter toutes les informations sur les methodes a convertir et leur liaison avec 
 * le type des elements voulus.
 * 
 */
	
	String expectedType;
	HashMap<String, String> types;
	ArrayList<Meth> methods;
	ArrayList<String> methDefault; // pour les method dans les maybes
	String className;
	
	class Meth {
		String name;
		String ret;
		ArrayList<String> args;
		
		@Override
		public boolean equals(Object obj) {
			if (obj instanceof Meth) {
				Meth n = (Meth) obj;
				return (n.name.equals(name));// && n.ret.equals(ret) && args.containsAll(n.args) && n.args.containsAll(args));
			}
			return false;
		}
		
		@Override
		public String toString() {
			return name;
		}
	}
	
	/**
	 * 
	 */
	public BuildGrammarTransformerClass() {
		methods = new ArrayList<BuildGrammarTransformerClass.Meth>();
		types = new HashMap<String, String>();
		methDefault = new ArrayList<String>();
	}

	
	private static String streamToString(String string) throws IOException {
		DataInputStream dis =  new DataInputStream(new FileInputStream(string));
		StringBuilder sb = new StringBuilder();
		byte[] b = new byte[2048];
		while((dis.read(b)) >= 0) {
			sb.append(new String(b));
		} ;
		return sb.toString();
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			GenericGrammar generic = new GenericGrammar();
			//generic.setMainParser(generic.fetch("keywords"));
			System.out.println(generic.toString());
			StringParseInput input = new StringParseInput(streamToString("testgengram.txt"));
			DefaultParseContext ctx = new DefaultParseContext();
			ParseResult<?> obj = generic.parse(ctx,input);
			if(obj.isError()) {
				System.out.println(ctx.deepest().getDetailedErrorMessage());
				System.out.println(ctx.deepest().getErrorMessage());
			}
			else if(obj.isNull()) {
				System.out.println("C un NULL");
			}
			else {
				System.out.println(obj.getResult());
				BuildGrammarTransformerClass gen = new BuildGrammarTransformerClass();
				List<GrammarTree> result = (List<GrammarTree>) obj.getResult();
				gen.init(result);
				System.out.println("Grammar: "+gen);
				gen.treatment(null);
				
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (GrammarVisitorException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void init(List<GrammarTree> grammar) throws GrammarVisitorException {
		for (GrammarTree grammarTree : grammar) {
			grammarTree.visit(this);
		}
		
	}

	@Override
	public String treatment(ParseInput input) throws GrammarVisitorException {
		StringBuilder sb = new StringBuilder("public class ");
		sb.append(className);
		sb.append("{\n");
		sb.append("\t// -- Methods for default method values\n");
		for (String m : methDefault) {
			sb.append("\tObject ");
			sb.append(m);
			sb.append("() {\n\t\t// TODO complete this method\n\t\treturn null;\n\t}\n");
		}
		sb.append("\n\t// -- Methods for default method values\n");
		for (Meth m : methods) {
			//sb.append("\tObject ");
			sb.append("\t public ");
			sb.append(m.ret);
			sb.append(" ");
			sb.append(m.name);
			sb.append("(Object content) {\n\t\t// TODO complete this method\n\t\treturn null;\n\t}\n");
		}
		sb.append("\n}");
		System.out.println(sb.toString());
		return sb.toString();
	}

	@Override
	public String visit(GChoice o) throws GrammarVisitorException {
		String t = null;
		for(GParser p : o.getChoices()) {
			t = moreGeneral(p.visit(this),t);
		}	
		return null;
	}

	private String moreGeneral(String visit, String t) {
		return visit;
	}


	@Override
	public String visit(GDefRegle r) throws GrammarVisitorException {
		types.put(r.getName(), r.getType());
		expectedType = r.getType();
		return expectedType;
	}

	@Override
	public String visit(GExprBracket b) throws GrammarVisitorException {
		
		return null;
	}

	@Override
	public String visit(GExprOperand o) throws GrammarVisitorException {
		o.getOperand().visit(this);
		return "javapop.framework.parser.expr.OperandNode";
	}

	@Override
	public String visit(GExprOperator o) throws GrammarVisitorException {
		o.getOperator().visit(this);
		return null;
	}

	@Override
	public String visit(GExprParser o) throws GrammarVisitorException {
		types.put(o.getName(), "javapop.framework.parser.expr.ExprNode");
		return null;
	}

	@Override
	public String visit(GIgnore o) throws GrammarVisitorException {
		o.getParser().visit(this);
		return null;
	}

	@Override
	public String visit(GKeyword o) throws GrammarVisitorException {
		
		return null;
	}

	@Override
	public String visit(GListOf o) throws GrammarVisitorException {
		
		o.getParser().visit(this);
		return null;
	}

	@Override
	public String visit(GLiteral o) throws GrammarVisitorException {
		
		return null;
	}

	@Override
	public String visit(GMaybeOrDefault o) throws GrammarVisitorException {
		o.getParser().visit(this);
		if(!methDefault.contains(o.getFctDefault()))
			methDefault.add(o.getFctDefault());
		return null;
	}
	
	@Override
	public String visit(GMaybe g) throws GrammarVisitorException {
		g.getParser().visit(this);
		return null;
	}


	@Override
	public String visit(GMeta o) throws GrammarVisitorException {
		if(o.getProperty().equals("grammar"))
			className = o.getValue();
		return null;
	}

	@Override
	public String visit(GRefParser o) throws GrammarVisitorException {
		// Ici peut etre faire une verif
		return null;
	}

	@Override
	public String visit(GRegexp o) throws GrammarVisitorException {
		return null;
	}

	@Override
	public String visit(GRegexpWithoutKeywords o) throws GrammarVisitorException {
		return null;
	}

	@Override
	public String visit(GRegle o) throws GrammarVisitorException {
		visit(o.getRegle());
		o.getParser().visit(this);
		
		return null;
	}

	@Override
	public String visit(GTransform o) throws GrammarVisitorException {
		Meth m = new Meth();
		m.name = o.getFct();
		m.ret = expectedType;
		if(!methods.contains(m))
			methods.add(m);
		// TODO plus complique garder en meme les types des elements qu'on traverse
		//m.ret = lastTypeRegle;
		
		o.getParser().visit(this);
		return null;
	}

	@Override
	public String visit(GTuple o) throws GrammarVisitorException {
		for (GParser p : o.getList()) {
			p.visit(this);
		}
		return null;
	}

	@Override
	public String visit(GKeywords o) throws GrammarVisitorException {
		return null;
	}


}
